package cn.graydove.bot.listener;

import cn.graydove.bot.adapter.GroupMessageEvent;
import cn.graydove.bot.adapter.RobotClient;
import cn.graydove.bot.annotation.Listener;
import cn.graydove.bot.core.Assert;
import cn.graydove.bot.enums.Boss;
import cn.graydove.bot.model.entity.QqGroup;
import cn.graydove.bot.service.BossManager;
import cn.graydove.bot.data.GroupServiceCacheData;
import cn.graydove.bot.model.entity.Mock;
import cn.graydove.bot.model.entity.User;
import cn.graydove.bot.support.WebProperties;
import cn.graydove.bot.utils.CommonUtils;
import cn.graydove.bot.utils.UrlUtils;
import net.mamoe.mirai.message.data.At;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Component
public class BossListener {
    private Assert asserts;
    private BossManager bossManager;
    private GroupServiceCacheData data;
    private WebProperties webProperties;

    private static final Object o = new Object();

    public BossListener(Assert asserts, BossManager bossManager, GroupServiceCacheData data, WebProperties webProperties) {
        this.asserts = asserts;
        this.bossManager = bossManager;
        this.data = data;
        this.webProperties = webProperties;
    }

    @Listener("^申请出刀$")
    public void attack(GroupMessageEvent event, RobotClient client) {
        asserts.groupReceive(event, (group) -> {
            synchronized (o) {
                if (!data.isAttacking(group.getGroupNumber())) {
                    User user = data.findUser(group, event.getUserId());
                    if (user.getOnTree()) {
                        client.sendGroupMsg(event.getGroupId(), "您已挂树。");
                        return;
                    }
                    Integer baseLine = group.getBaseLine();
                    if (baseLine != null && baseLine > group.getCurrentHealth()) {
                        client.sendGroupMsg(event.getGroupId(), CommonUtils.append(new At(event.getSender())).append("Boss血量已低于血线，请等待管理员指挥合刀。"));
                        return;
                    }
                    data.setAttacking(group, event.getUserId());
                    String msg = CommonUtils.getName(event) + " 已开始boss挑战，其他玩家请稍后。\n" + bossManager.getMessage(group);
                    client.sendGroupMsg(event.getGroupId(), msg);
                } else {
                    User attacker = data.getAttacker(group);

                    if (attacker == null) {
                        client.sendGroupMsg(event.getGroupId(), "出刀太频繁，处理不过来了......");
                        return;
                    }

                    if (attacker.getQqNumber().equals(event.getUserId())) {
                        client.sendGroupMsg(event.getGroupId(), "您上一次的出刀未完成。");
                        return;
                    }
                    client.sendGroupMsg(event.getGroupId(), attacker.getName() + "正在挑战Boss，请稍后。");
                }
            }
        });

    }

    @Listener("^完成")
    public void attackFinish(GroupMessageEvent event, RobotClient client) {
        asserts.groupReceive(event, (group) -> {
            if (!Objects.equals(group.getAttacker(), event.getUserId())) {
                return;
            }
            try {
                int dmg = Math.toIntExact(client.getNumber());

                bossManager.attack(group,data.getAttacker(group), dmg);
                data.setAttacking(group, null);

                String msg = CommonUtils.getName(event) + " 已完成boss挑战\n" + bossManager.getMessage(group);
                client.sendGroupMsg(event.getGroupId(), msg);
            } catch (Exception e) {
                client.sendGroupMsg(event.getGroupId(), "请输入正确的伤害量");
            }
        });

    }

    @Listener("^查看$")
    public void showBoss(GroupMessageEvent event, RobotClient client) {
        asserts.groupReceive(event, (group) -> client.sendGroupMsg(event.getGroupId(), bossManager.getMessage(group)));
    }

    @Listener("^挂树$")
    public void upTree(GroupMessageEvent event, RobotClient client) {
        asserts.groupReceive(event, (group) -> {
            if (Objects.equals(event.getUserId(), group.getAttacker())) {
                data.setAttacking(group, null);
            }
            data.setTree(group, event.getUserId(), true);
            String msg = CommonUtils.getName(event) + " 已挂树\n" + bossManager.getMessage(group);
            client.sendGroupMsg(event.getGroupId(), msg);
        });
    }

    @Listener("^查树$")
    public void showTree(GroupMessageEvent event, RobotClient client) {
        asserts.groupReceive(event, (group) -> {
            StringBuilder sb = new StringBuilder("当前挂树成员：\n");
            List<User> collect = group.getUsers().stream().filter(User::getOnTree).collect(Collectors.toList());
            for (var u : collect) {
                sb.append(u.getName()).append("\n");
            }
            client.sendGroupMsg(event.getGroupId(), sb.toString());
        });
    }

    @Listener("^强行下树")
    public void downTree(GroupMessageEvent event, RobotClient client) {
        asserts.groupReceive(event, (group) -> {
            User u = data.findUser(group, event.getUserId());
            if (u != null && u.getOnTree()) {
                try {
                    int dmg = Math.toIntExact(client.getNumber());

                    bossManager.attack(group, u, dmg);
                    data.setTree(group, event.getUserId(), false);

                    client.sendGroupMsg(event.getGroupId(), bossManager.getMessage(group));
                } catch (Exception e) {
                    client.sendGroupMsg(event.getGroupId(), "请输入正确的伤害量");
                }

            }
        });

    }

    @Listener("^修正 [\\d]+ [\\d]+ [\\d]+$")
    public void fix(GroupMessageEvent event, RobotClient client) {
        asserts.groupAdminReceive(event, (group) -> {
            int r, n ,hp;
            String[] s = event.getMessage().split(" ");
            try {
                r = Integer.parseInt(s[1]);
                n = Integer.parseInt(s[2]);
                hp = Integer.parseInt(s[3]);
                n -= 1;
                if (r <= 0 || hp <=0 || n < 0 || n >= Boss.values().length) {
                    throw new RuntimeException();
                }
                bossManager.reset(group, r, n, hp);
                showBoss(event, client);
            } catch (Exception e) {
                client.sendGroupMsg(event.getGroupId(), "请按“周目 Boss 生命”输入正确数字。");
            }
        });

    }

    @Listener("^出刀统计$")
    public void showStatistic(GroupMessageEvent event, RobotClient client) {
        asserts.groupReceive(event, (group) -> {
            StringBuilder stringBuilder = new StringBuilder("出刀情况统计:\n");
            List<User> users = group.getUsers().stream().filter(user -> user.getAttack() > 0).collect(Collectors.toList());
            int c = 0;
            for (var u: users) {
                c += u.getAttack();
            }
            stringBuilder
                    .append("\t共")
                    .append(users.size())
                    .append("人出刀，")
                    .append("共出")
                    .append(c)
                    .append("刀\n");
            for (var u: users) {
                stringBuilder
                        .append(u.getName())
                        .append(" 已出刀")
                        .append(u.getAttack())
                        .append("次，共造成")
                        .append(u.getDamage())
                        .append("伤害")
                        .append("\n");
            }
            client.sendGroupMsg(event.getGroupId(), stringBuilder.toString());
        });

    }


    @Listener("^未出刀统计$")
    public void showNotAttackStatistic(GroupMessageEvent event, RobotClient client) {
        asserts.groupReceive(event, (group) -> {
            List<Long> members = new ArrayList<>();
            for (var i : event.getClient().getSubject().getMembers()) {
                group = data.ensureUserAndGetGroup(group, client, i.getId());
                members.add(i.getId());
            }

            StringBuilder stringBuilder = new StringBuilder("未出刀情况统计:\n");
            List<User> users = group.getUsers().stream().filter(user ->
                        user.getAttack() < 3 &&
                        !Objects.equals(user.getQqNumber(), event.getSelfId())&&
                        CommonUtils.in(user.getQqNumber(), members)
                    )
                    .collect(Collectors.toList());
            int c = 0;
            for (var u: users) {
                c += 3 - u.getAttack();
            }
            stringBuilder
                    .append("\t共")
                    .append(users.size())
                    .append("人未出刀，")
                    .append("共未出")
                    .append(c)
                    .append("刀\n");
            for (var u: users) {
                stringBuilder
                        .append(u.getName())
                        .append(" 已出刀")
                        .append(u.getAttack())
                        .append("次\n");
            }
            client.sendGroupMsg(event.getGroupId(), stringBuilder.toString());
        });

    }


    @Listener("^模拟 [\\d]+ [\\d]+ [\\d]+$")
    public void mock(GroupMessageEvent event, RobotClient client) {
        asserts.groupReceive(event, (group) -> {
            int r, n ,dmg;
            String[] s = event.getMessage().split(" ");
            try {
                r = Integer.parseInt(s[1]);
                n = Integer.parseInt(s[2]);
                dmg = Integer.parseInt(s[3]);
                n -= 1;
                if (r <= 0 || dmg <=0 || n < 0 || n >= Boss.values().length) {
                    throw new RuntimeException();
                }
                Mock mock = new Mock(event, r, bossManager.getBoss(n), dmg);
                data.addMock(group, mock);
                client.sendGroupMsg(event.getGroupId(), "模拟战中 " + mock.toString());
            } catch (Exception e) {
                client.sendGroupMsg(event.getGroupId(), "请按“周目 Boss 伤害”输入正确数字。");
            }
        });

    }

    @Listener("^模拟统计$")
    public void mockStatistic(GroupMessageEvent event, RobotClient client) {
        asserts.groupReceive(event, (group) -> {
            StringBuilder stringBuilder = new StringBuilder("Boss情况统计:\n");
            for (var u: group.getMocks()) {
                stringBuilder.append(u.toString()).append('\n');
            }
            client.sendGroupMsg(event.getGroupId(), stringBuilder.toString());
        });

    }

    @Listener("^删除模拟记录$")
    public void deleteMockStatistic(GroupMessageEvent event, RobotClient client) {
        asserts.groupReceive(event, (group) -> {
            long userId = event.getUserId();

            List<Mock> removes = group.getMocks().stream().filter(mock -> mock.getUserId() == userId).collect(Collectors.toList());
            data.removeMocks(group, removes);
            client.sendGroupMsg(event.getGroupId(), "已清除 " + CommonUtils.getName(event) + " 的模拟记录");
        });

    }

    @Listener("^清空模拟记录$")
    public void clearMockStatistic(GroupMessageEvent event, RobotClient client) {
        asserts.groupAdminReceive(event, (group) -> {
            Collection<Mock> mocks = group.getMocks();
            data.removeMocks(group, mocks);
            client.sendGroupMsg(event.getGroupId(), "已清除全部模拟记录");
        });

    }

    @Listener("^导出模拟Excel$")
    public void mockExcel(GroupMessageEvent event, RobotClient client) {
        asserts.groupAdminReceive(event, (group) -> {
            String url = UrlUtils.urlFormat(webProperties.getDomain());
            url = url + "/mock/" + group.getGroupNumber().toString();
            client.sendGroupMsg(event.getGroupId(), "Excel下载地址：" + url);
        });
    }

    @Listener("^导出出刀Excel$")
    public void attackExcel(GroupMessageEvent event, RobotClient client, HttpServletRequest request) {
        asserts.groupAdminReceive(event, (group) -> {
            String url = UrlUtils.urlFormat(webProperties.getDomain());
            url = url + "/attack/" + group.getGroupNumber().toString();
            client.sendGroupMsg(event.getGroupId(), "Excel下载地址：" + url);
        });
    }

    @Listener("^开启准备统计$")
    public void startCoAttackStatistic(GroupMessageEvent event, RobotClient client) {
        asserts.groupAdminReceive(event, (group) -> {
            try {
                group.setReadyStatistic(true);
                data.saveGroup(group);
                client.sendGroupMsg(event.getGroupId(), "已开启准备统计。");
            } catch (Exception e) {
                client.sendGroupMsg(event.getGroupId(), "系统出错");
            }
        });
    }


    @Listener("^结束准备统计$")
    public void stopCoAttackStatistic(GroupMessageEvent event, RobotClient client) {
        asserts.groupAdminReceive(event, (group) -> {
            try {
                for (var u: group.getUsers()) {
                    u.setReady(false);
                    data.saveUser(u);
                }
                group.setReadyStatistic(false);
                data.saveGroup(group);
                client.sendGroupMsg(event.getGroupId(), "已结束准备统计。");
            } catch (Exception e) {
                client.sendGroupMsg(event.getGroupId(), "系统出错");
            }
        });
    }

    @Listener("^查看准备统计$")
    public void showCoAttackStatistic(GroupMessageEvent event, RobotClient client) {
        asserts.groupReceive(event, (group) -> {
            try {
                Set<User> users = group.getUsers().stream().filter(user -> user.getReady() != null && user.getReady()).collect(Collectors.toSet());

                StringBuilder sb = new StringBuilder();
                sb.append("共").append(users.size()).append("位成员已经准备好\n").append("成员如下:\n");
                for (var u: users) {
                    sb.append("\t").append(u.getName()).append("\n");
                }
                client.sendGroupMsg(event.getGroupId(), sb.toString());
            } catch (Exception e) {
                client.sendGroupMsg(event.getGroupId(), "系统出错");
            }
        });
    }

    @Listener("^准备$")
    public void readyCoAttackStatistic(GroupMessageEvent event, RobotClient client) {
        asserts.groupReceive(event, (group) -> {
            try {
                User user = data.findUser(group, event.getUserId());
                if (user.getReady() == null || !user.getReady()) {
                    user.setReady(true);
                    data.saveUser(user);
                    showCoAttackStatistic(event, client);
                }
            } catch (Exception e) {
                client.sendGroupMsg(event.getGroupId(), "系统出错");
            }
        });
    }

    @Listener("^取消准备$")
    public void unreadyStatistic(GroupMessageEvent event, RobotClient client) {
        asserts.groupReceive(event, (group) -> {
            try {
                User user = data.findUser(group, event.getUserId());
                if (user.getReady() != null && user.getReady()) {
                    user.setReady(false);
                    data.saveUser(user);
                    showCoAttackStatistic(event, client);
                }
            } catch (Exception e) {
                client.sendGroupMsg(event.getGroupId(), "系统出错");
            }
        });
    }

    @Listener("^设置血线")
    public void setHpBaseline(GroupMessageEvent event, RobotClient client) {
        asserts.groupAdminReceive(event, (group) -> {
            try {
                int hp = Math.toIntExact(client.getNumber());
                group.setBaseLine(hp);
                QqGroup qqGroup = data.saveGroup(group);
                client.sendGroupMsg(event.getGroupId(), "当前Boss血线已设置为" + qqGroup.getBaseLine());
            } catch (Exception e) {
                client.sendGroupMsg(event.getGroupId(), "设置失败");
            }
        });
    }


    @Listener("^补偿刀")
    public void compensateAttack(GroupMessageEvent event, RobotClient client) {
        asserts.groupReceive(event, (group) -> {
            if (!Objects.equals(group.getAttacker(), event.getUserId())) {
                return;
            }
            try {
                int dmg = Math.toIntExact(client.getNumber());

                bossManager.attack(group,data.getAttacker(group), dmg, true);
                data.setAttacking(group, null);

                String msg = CommonUtils.getName(event) + " 已完成boss挑战\n" + bossManager.getMessage(group);
                client.sendGroupMsg(event.getGroupId(), msg);
            } catch (Exception e) {
                client.sendGroupMsg(event.getGroupId(), "请输入正确的伤害量");
            }
        });

    }

    @Listener("^代刀")
    public void proxyAttack(GroupMessageEvent event, RobotClient client) {
        asserts.groupReceive(event, (group) -> {
            if (!Objects.equals(group.getAttacker(), event.getUserId())) {
                return;
            }
            try {

                long qq = client.getAt().getTarget();

                int dmg = Math.toIntExact(client.getNumber());

                group = data.ensureUserAndGetGroup(group, client, qq);
                User attacker = data.findUser(group, qq);

                bossManager.attack(group, attacker, dmg);
                data.setAttacking(group, null);

                String msg = attacker.getName() + " 已完成boss挑战\n" + bossManager.getMessage(group);
                client.sendGroupMsg(event.getGroupId(), msg);
            } catch (Exception e) {
                client.sendGroupMsg(event.getGroupId(), "记录失败");
            }
        });

    }

    @Listener("^代刀补偿刀")
    public void proxyCompensateAttack(GroupMessageEvent event, RobotClient client) {
        asserts.groupReceive(event, (group) -> {
            if (!Objects.equals(group.getAttacker(), event.getUserId())) {
                return;
            }
            try {
                long qq = client.getAt().getTarget();

                int dmg = Math.toIntExact(client.getNumber());

                group = data.ensureUserAndGetGroup(group, client, qq);
                User attacker = data.findUser(group, qq);

                bossManager.attack(group, attacker, dmg, true);
                data.setAttacking(group, null);

                String msg = attacker.getName() + " 已完成boss挑战\n" + bossManager.getMessage(group);
                client.sendGroupMsg(event.getGroupId(), msg);
            } catch (Exception e) {
                client.sendGroupMsg(event.getGroupId(), "记录失败");
            }
        });

    }

    @Listener("^报刀")
    public void attackFinishWithoutApply(GroupMessageEvent event, RobotClient client) {
        asserts.groupReceive(event, (group) -> {
            try {
                int dmg = Math.toIntExact(client.getNumber());

                User attacker = data.findUser(group, event.getUserId());
                bossManager.attack(group, attacker, dmg);
                if (attacker.getQqNumber().equals(group.getAttacker()))
                    data.setAttacking(group, null);

                String msg = CommonUtils.getName(event) + " 已完成boss挑战\n" + bossManager.getMessage(group);
                client.sendGroupMsg(event.getGroupId(), msg);
            } catch (Exception e) {
                client.sendGroupMsg(event.getGroupId(), "请输入正确的伤害量");
            }
        });

    }

    @Listener("^代报刀")
    public void proxyAttackFinishWithoutApply(GroupMessageEvent event, RobotClient client) {
        asserts.groupReceive(event, (group) -> {
            try {
                long qq = client.getAt().getTarget();

                int dmg = Math.toIntExact(client.getNumber());

                group = data.ensureUserAndGetGroup(group, client, qq);
                User attacker = data.findUser(group, qq);

                bossManager.attack(group, attacker, dmg);

                if (attacker.getQqNumber().equals(group.getAttacker()))
                    data.setAttacking(group, null);

                String msg = attacker.getName() + " 已完成boss挑战\n" + bossManager.getMessage(group);
                client.sendGroupMsg(event.getGroupId(), msg);
            } catch (Exception e) {
                client.sendGroupMsg(event.getGroupId(), "记录失败");
            }
        });

    }

}
